/**
 * Copyright 1999-2015 dangdang.com.
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * </p>
 */

package org.aceor.mddal.gateway.executor;

import org.aceor.mddal.common.executor.ExecuteUnit;
import org.aceor.mddal.gateway.executor.impl.DefaultQueryMergeUnit;
import org.aceor.mddal.gateway.executor.impl.DefaultUpdateMergeUnit;
import org.apache.commons.collections.CollectionUtils;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Collection;
import java.util.List;

/**
 * 多线程执行静态语句对象请求的执行器.
 *
 * @author gaohongtao
 */
public final class StatementExecutor {

    private final ExecutorEngine executorEngine;

    private final Collection<StatementEntity> statements;

    private final ExecuteContext executeContext;

    private StatementExecutor(ExecuteContext executeContext, Collection<StatementEntity> statementEntities) {
        this.executorEngine = ExecutorEngineFactory.getInstance();
        this.statements = statementEntities;
        this.executeContext = executeContext;
    }

    /**
     * 执行SQL查询.
     *
     * @return 结果集列表
     * @throws SQLException SQL异常
     */
    public ResultSet executeQuery() throws SQLException {
        ResultSet result;
        if (1 == statements.size()) {
            StatementEntity entity = statements.iterator().next();
            result = entity.statement.executeQuery(entity.sql);
        } else {
            result = executorEngine.execute(statements, new ExecuteUnit<StatementEntity, ResultSet>() {

                @Override
                public ResultSet execute(final StatementEntity input) throws Exception {
                    return input.statement.executeQuery(input.sql);
                }
            }, new DefaultQueryMergeUnit(executeContext.mergeContext));
        }
        return result;
    }

    /**
     * 执行SQL更新.
     *
     * @return 更新数量
     * @throws SQLException SQL异常
     */
    public int executeUpdate() throws SQLException {
        return executeUpdate(new Updater() {

            @Override
            public int executeUpdate(final Statement statement, final String sql) throws SQLException {
                return statement.executeUpdate(sql);
            }
        });
    }

    public int executeUpdate(final int autoGeneratedKeys) throws SQLException {
        return executeUpdate(new Updater() {

            @Override
            public int executeUpdate(final Statement statement, final String sql) throws SQLException {
                return statement.executeUpdate(sql, autoGeneratedKeys);
            }
        });
    }

    public int executeUpdate(final int[] columnIndexes) throws SQLException {
        return executeUpdate(new Updater() {

            @Override
            public int executeUpdate(final Statement statement, final String sql) throws SQLException {
                return statement.executeUpdate(sql, columnIndexes);
            }
        });
    }

    public int executeUpdate(final String[] columnNames) throws SQLException {
        return executeUpdate(new Updater() {

            @Override
            public int executeUpdate(final Statement statement, final String sql) throws SQLException {
                return statement.executeUpdate(sql, columnNames);
            }
        });
    }

    /**
     * map-reduce
     * @param updater
     * @return
     * @throws SQLException
     */
    private int executeUpdate(final Updater updater) throws SQLException {
        int result;
        if (1 == statements.size()) {
            StatementEntity entity = statements.iterator().next();
            result = updater.executeUpdate(entity.statement, entity.sql);
        } else {
            result = executorEngine.execute(statements, new ExecuteUnit<StatementEntity, Integer>() {

                @Override
                public Integer execute(final StatementEntity input) throws Exception {
                    return updater.executeUpdate(input.statement, input.sql);
                }
            }, new DefaultUpdateMergeUnit());
        }
        return result;
    }

    /**
     * 执行SQL请求.
     *
     * @return true表示执行DQL语句, false表示执行的DML语句
     * @throws SQLException SQL异常
     */
    public boolean execute() throws SQLException {
        return execute(new Executor() {

            @Override
            public boolean execute(final Statement statement, final String sql) throws SQLException {
                return statement.execute(sql);
            }
        });
    }

    public boolean execute(final int autoGeneratedKeys) throws SQLException {
        return execute(new Executor() {

            @Override
            public boolean execute(final Statement statement, final String sql) throws SQLException {
                return statement.execute(sql, autoGeneratedKeys);
            }
        });
    }

    public boolean execute(final int[] columnIndexes) throws SQLException {
        return execute(new Executor() {

            @Override
            public boolean execute(final Statement statement, final String sql) throws SQLException {
                return statement.execute(sql, columnIndexes);
            }
        });
    }

    public boolean execute(final String[] columnNames) throws SQLException {
        return execute(new Executor() {

            @Override
            public boolean execute(final Statement statement, final String sql) throws SQLException {
                return statement.execute(sql, columnNames);
            }
        });
    }

    private boolean execute(final Executor executor) throws SQLException {
        if (1 == statements.size()) {
            StatementEntity entity = statements.iterator().next();
            boolean result = executor.execute(entity.statement, entity.sql);
            return result;
        }
        List<Boolean> result = executorEngine.execute(statements, new ExecuteUnit<StatementEntity, Boolean>() {

            @Override
            public Boolean execute(final StatementEntity input) throws Exception {
                return executor.execute(input.statement, input.sql);
            }
        });
        return result.get(0);
    }

    /**
     * 策略模式
     */
    private interface Updater {

        int executeUpdate(Statement statement, String sql) throws SQLException;
    }

    private interface Executor {

        boolean execute(Statement statement, String sql) throws SQLException;
    }

    public static class StatementEntity {

        private final String sql;

        private final Statement statement;

        public StatementEntity(String sql, Statement statement) {
            this.sql = sql;
            this.statement = statement;
        }
    }

    public static StatementExecutorHolder build() {
        return new StatementExecutorHolder();
    }

    /**
     * 用于支持函数式创建StatementExecutor对象
     */
    public static class StatementExecutorHolder{
        private ExecuteContext executeContext;
        private Collection<StatementEntity> statementEntities;

        public StatementExecutorHolder setStatementEntities(Collection<StatementEntity> statementEntityCollection) {
            this.statementEntities = statementEntityCollection;
            return this;
        }

        public StatementExecutorHolder setExecuteContext(ExecuteContext executeContext) {
            this.executeContext = executeContext;
            return this;
        }

        public StatementExecutor create() {
            if (null == executeContext) {
                throw new RuntimeException("ExecuteContext 不能为空");
            }
            if (CollectionUtils.isEmpty(statementEntities)) {
                throw new RuntimeException("Statement集合不能为空");
            }
            return new StatementExecutor(executeContext, statementEntities);
        }
    }
}
